﻿using System;
using System.Windows.Input;

namespace Faz.SideWinderSC.Client.ViewModel
{
    /// <summary>
    /// Implements the <see cref="ICommand"/> interface with delegates.
    /// </summary>
    public class Command : ICommand
    {
        /// <summary>
        /// The delegate associated with the <see cref="ICommand.Execute"/> method.
        /// </summary>
        private readonly Action<object> execute;

        /// <summary>
        /// The delegate associated with the <see cref="ICommand.CanExecute"/> method.
        /// </summary>
        private readonly Func<object, bool> canExecute;

        /// <summary>
        /// Initializes a new instance of the <see cref="Command"/> class
        /// that can always be executed.
        /// </summary>
        /// <param name="execute">
        /// The delegate associated with the <see cref="ICommand.Execute"/> method.
        /// </param>
        public Command(Action<object> execute)
            : this(execute, null)
        { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Command"/> class.
        /// </summary>
        /// <param name="execute">
        /// The delegate associated with the <see cref="ICommand.Execute"/> method.
        /// </param>
        /// <param name="canExecute">
        /// The delegate associated with the <see cref="ICommand.CanExecute"/> method - if any.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="execute"/> parameter is <c>null</c>.
        /// </exception>
        public Command(Action<object> execute, Func<object, bool> canExecute)
        {
            if (execute == null)
            {
                throw new ArgumentNullException("execute");
            }

            this.execute = execute;
            this.canExecute = canExecute;
        }

        /// <summary>
        /// Occurs when changes occur that affect whether or not the command should execute.
        /// </summary>
        /// <remarks>
        /// Associated to <see cref="CommandManager.RequerySuggested"/>.
        /// </remarks>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        /// <summary>
        /// Determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">
        /// Data used by the command. If the command does not require data to be passed,
        /// this object can be set to <c>null</c>.
        ///</param>
        /// <returns>
        /// <c>true</c> if this command can be executed;
        /// otherwise, <c>false</c>.
        /// </returns>
        public bool CanExecute(object parameter)
        {
            return this.canExecute == null || this.canExecute(parameter);
        }

        /// <summary>
        /// Called when the command is invoked.
        /// </summary>
        /// <param name="parameter">
        /// Data used by the command. If the command does not require data to be passed,
        /// this object can be set to <c>null</c>.
        ///</param>
        public void Execute(object parameter)
        {
            if (this.CanExecute(parameter))
            {
                this.execute(parameter);
            }
        }
    }
}
